#include <stdio.h>
#include <math.h>
#include <string.h>
#include "slave.h"

__thread_local volatile unsigned long get_reply,put_reply;
__thread_local double A_lhs[BLKX][BLKY];
__thread_local double A_rhs[BLKX+2][BLKY+2];
__thread_local_fix int my_task_id,my_id,my_task_num;
extern double A[2][SIZEX][SIZEY];
extern int thread_num, N, ntilex, ntiley;
extern int startj[64], starti[64];
extern int  ITER;
#define alpha_d 0.0876
#define beta_d  0.0765

__thread_local int my_id=0;
#ifdef PROFILING
__thread_local volatile unsigned long dma_start1,dma_end1,dma_end2;//dma_start3,dma_end3,dma_start4,dma_end4;
__thread_local volatile unsigned long comp_end1;//,comp_start2,comp_end2;
//__thread_local_fix int my_task_id,my_id,my_task_num,,;
extern unsigned long slave_comp_count[64];
extern unsigned long slave_dma_count[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif

void func()
{
     int i,j,ii,jj,k;//N ~ average num tiles that each thread assigned
     int dest,src;
     int my_tiles,my_iter;
     unsigned long dma_cnt=0,comp_cnt=0;
     my_id = athread_get_id(-1);
     //1. compute my num tiles
     if(my_id>=thread_num)
     {
        //some dummy threads, since we do not know how to synchronize between threads which is less than 64.
        my_task_num=0;
     }
     else if(my_id >= ((ntilex * ntiley)%thread_num) )
     {
        my_task_num = (ntilex * ntiley) /thread_num;
     }
     else //if (my_id<=thread_num)
     {
        my_task_num=(ntilex * ntiley) /thread_num + 1;
     }
     //2. begin time iteration
     for(my_iter=0; my_iter<ITER; my_iter++)
     {
     dest = my_iter % 2 ;
     src = 1-dest;
     for (k=0; k<my_task_num; k++)
     {
         //mapping strategy: cyclic(1) distribution
         //0,1,2,3,0,1,2,3,0,1
         //2,3,0,1,2,3,0,1,2,3
         //0,1,2,3,0,1,2,3,0,1
         //j is the lower dimension
         int stride,tsize,bsize,idma;
         my_task_id=my_id+k*(thread_num);//BLOCK ID
         i = my_task_id / (ntilex) * BLKX;
         j = my_task_id % (ntiley) * BLKY;
         //update data, <BLKX+2,BLKY+2>
#ifdef PROFILING
  dma_start1=rtc_();
#endif
         get_reply=0;
         bsize=(BLKY+2)*8;
         stride=(SIZEY-(BLKY+2))*8;  //stride: from blocktail to blockhead 
         tsize= bsize*(BLKX+2);
         athread_get(PE_MODE,&A[src][i][j]
                 , &A_rhs[0][0]
                 , tsize,(void*)&get_reply
                 , 0, stride, bsize);
         while(get_reply!=1);
#ifdef PROFILING
  dma_end1=rtc_();
#endif
         //compute 
         for(ii = 1; ii < BLKX+1; ++ii) 
           for(jj = 1; jj < BLKY+1; ++jj) 
           {
                A_lhs[ii-1][jj-1] = alpha_d * (A_rhs[ii][jj]) + \
                     beta_d * (A_rhs[ii-1][jj] + A_rhs[ii+1][jj] +\
                     A_rhs[ii][jj-1] + A_rhs[ii][jj+1]);
           }
#ifdef PROFILING
  comp_end1=rtc_();
#endif
         //write back
         put_reply=0;
         stride = (SIZEY-BLKY)*8;
         bsize = BLKY*8;
         tsize = bsize*BLKX;
         athread_put(PE_MODE,&A_lhs[0][0]
                     , &A[dest][i+1][j+1]
                     ,tsize,(void*)&put_reply,stride,bsize);
         while(put_reply!=1);
#ifdef PROFILING
  dma_end2=rtc_();
  dma_cnt+=(dma_end1-dma_start1+dma_end2-comp_end1);  
  comp_cnt+=((comp_end1-dma_end1));  
#endif
     }//my tiles
  athread_syn(ARRAY_SCOPE,-1);
  }//time iterations
  slave_dma_count[my_id]+=dma_cnt;
  slave_comp_count[my_id]+=comp_cnt;
}
